{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit SettingsForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls, DirectShowEnumerator, MediaFolders,
  MediaFolderManager;

const
  MSG_CONFIRM_DEL = 'Are you sure want to remove this directory from the media ' +
                    'repository?' + sLineBreak + sLineBreak + 'Once removed you' +
                    ' will not be able to view any of the media within mediate.' +
                    sLineBreak + sLineBreak + 'Note: This does not delete your ' +
                    'actual files on disk.';

type
  TfrmSettings = class(TForm)
    btSave: TButton;
    btCancel: TButton;
    TreeView: TTreeView;
    Notebook: TNotebook;
    pnControlControls: TPanel;
    gbInput: TGroupBox;
    Label4: TLabel;
    Label5: TLabel;
    lbInputDescription: TLabel;
    ecKeybinding: TComboBox;
    btViewProfile: TButton;
    gbMouse: TGroupBox;
    Label6: TLabel;
    Label8: TLabel;
    ecMouse: TComboBox;
    ecRButton: TComboBox;
    pnGeneralControls: TPanel;
    gbLogging: TGroupBox;
    Label3: TLabel;
    ecLogLevel: TComboBox;
    cbDebug: TCheckBox;
    cbSaveGraph: TCheckBox;
    cbCache: TCheckBox;
    gbScreenSaver: TGroupBox;
    Label10: TLabel;
    edScreenSaver: TEdit;
    btBrowse: TButton;
    pnAudioFormats: TPanel;
    pnPlaybackControls: TPanel;
    gbAudioSettings: TGroupBox;
    Label11: TLabel;
    ecAudioRenderer: TComboBox;
    gbDVDSettings: TGroupBox;
    Label12: TLabel;
    Label13: TLabel;
    ecDVDAudio: TComboBox;
    ecDVDVideo: TComboBox;
    btDVDAudioProps: TButton;
    btDVDVideoProps: TButton;
    pnVideoFormats: TPanel;
    pnHeading: TPanel;
    gbAudioFormats: TGroupBox;
    lvAudio: TListView;
    btAddAudio: TButton;
    btDeleteAudio: TButton;
    Label1: TLabel;
    lbAudioSupport: TLabel;
    gbVideoFormats: TGroupBox;
    btDeleteVideo: TButton;
    btAddVideo: TButton;
    lvVideo: TListView;
    Label2: TLabel;
    lbV: TLabel;
    ecVideoSettings: TGroupBox;
    Label9: TLabel;
    ecVideoRenderer: TComboBox;
    pnInternetSettings: TPanel;
    gbProxy: TGroupBox;
    cbUseProxy: TCheckBox;
    gbInternet: TGroupBox;
    cbCheckVersion: TCheckBox;
    Label15: TLabel;
    Label16: TLabel;
    Label17: TLabel;
    Label18: TLabel;
    edProxyServer: TEdit;
    edProxyUser: TEdit;
    edProxyPort: TEdit;
    edProxyPass: TEdit;
    udProxyPort: TUpDown;
    pnDisplaySettings: TPanel;
    gbWindow: TGroupBox;
    cbHideBorder: TCheckBox;
    cbMaximize: TCheckBox;
    gbDirectX: TGroupBox;
    Label19: TLabel;
    ecTextureFormat: TComboBox;
    Label20: TLabel;
    cbExclusive: TCheckBox;
    btApply: TButton;
    lbSupport: TLabel;
    Label21: TLabel;
    Label7: TLabel;
    ecScreenSaver: TComboBox;
    ecVideoDevice: TComboBox;
    Label14: TLabel;
    Label22: TLabel;
    pnPluginPanel: TPanel;
    gbPlugins: TGroupBox;
    Label23: TLabel;
    lbPluginsLink: TLabel;
    lvPlugins: TListView;
    btPluginAdd: TButton;
    btPluginDelete: TButton;
    btPluginProperties: TButton;
    pnThemeSettings: TPanel;
    gbThemeSettings: TGroupBox;
    Label24: TLabel;
    Label25: TLabel;
    Label26: TLabel;
    ecCurrentTheme: TComboBox;
    Button4: TButton;
    cbMaximumFPS: TCheckBox;
    edMaximumFPS: TEdit;
    Label27: TLabel;
    udMaximumFPS: TUpDown;
    gbFileSettings: TGroupBox;
    cbGroupDirs: TCheckBox;
    pnFolders: TPanel;
    gbFolders: TGroupBox;
    lvFolders: TListView;
    btAddFolder: TButton;
    btDeleteFolder: TButton;
    btEditFolder: TButton;
    pnWindowsSettings: TPanel;
    gbWindowsBehaviour: TGroupBox;
    cbBalloonTips: TCheckBox;
    btConfigurePlugin: TButton;
    pnSynchroniseSettings: TPanel;
    cbSaveConsole: TCheckBox;
    Label28: TLabel;
    ecAudioCapture: TComboBox;
    pbHeader: TPaintBox;
    Label29: TLabel;
    ecAudioMixing: TComboBox;
    ecFontQuality: TComboBox;
    Label30: TLabel;
    procedure lbAudioSupportClick(Sender: TObject);
    procedure lvAudioColumnClick(Sender: TObject; Column: TListColumn);
    procedure lvAudioCompare(Sender: TObject; Item1, Item2: TListItem;
      Data: Integer; var Compare: Integer);
    procedure lvVideoColumnClick(Sender: TObject; Column: TListColumn);
    procedure lvVideoCompare(Sender: TObject; Item1, Item2: TListItem;
      Data: Integer; var Compare: Integer);
    procedure btSaveClick(Sender: TObject);
    procedure ecKeybindingChange(Sender: TObject);
    procedure btViewProfileClick(Sender: TObject);
    procedure lvAudioClick(Sender: TObject);
    procedure lvVideoClick(Sender: TObject);
    procedure btAddVideoClick(Sender: TObject);
    procedure btDeleteVideoClick(Sender: TObject);
    procedure btDeleteAudioClick(Sender: TObject);
    procedure btAddAudioClick(Sender: TObject);
    procedure btBrowseClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btDVDVideoPropsClick(Sender: TObject);
    procedure btDVDAudioPropsClick(Sender: TObject);
    procedure ecDVDDecoderChange(Sender: TObject);
    procedure TreeViewChange(Sender: TObject; Node: TTreeNode);
    procedure cbUseProxyClick(Sender: TObject);
    procedure lbSupportClick(Sender: TObject);
    procedure ecScreenSaverChange(Sender: TObject);
    procedure lbPluginsLinkClick(Sender: TObject);
    procedure lvPluginsColumnClick(Sender: TObject; Column: TListColumn);
    procedure lvPluginsCompare(Sender: TObject; Item1, Item2: TListItem;
      Data: Integer; var Compare: Integer);
    procedure lvPluginsClick(Sender: TObject);
    procedure btPluginDeleteClick(Sender: TObject);
    procedure btPluginAddClick(Sender: TObject);
    procedure btPluginPropertiesClick(Sender: TObject);
    procedure cbMaximumFPSClick(Sender: TObject);
    procedure lvFoldersClick(Sender: TObject);
    procedure btAddFolderClick(Sender: TObject);
    procedure btEditFolderClick(Sender: TObject);
    procedure btDeleteFolderClick(Sender: TObject);
    procedure lvFoldersColumnClick(Sender: TObject; Column: TListColumn);
    procedure lvFoldersCompare(Sender: TObject; Item1, Item2: TListItem;
      Data: Integer; var Compare: Integer);
    procedure btConfigurePluginClick(Sender: TObject);
    procedure pbHeaderPaint(Sender: TObject);
  private
    { Private declarations }
    FUpdated: Boolean;
    FEnumerator: TAHMDirectShowEnumerator;
    FManager: TAHMMediaFolderManager;
    FHeader: TBitmap;
    FUpdatingList: Boolean;
    FSortAudioDescending: Boolean;
    FSortVideoDescending: Boolean;
    FSortPluginDescending: Boolean;
    FSortFolderDescending: Boolean;
    FSortAudioColumn: Integer;
    FSortVideoColumn: Integer;
    FSortPluginColumn: Integer;
    FSortFolderColumn: Integer;
    function CurrentType: TAHMMediaFolderType;
  protected
    { Protected declarations }
    procedure PopulatePlugins;
    procedure PopulateFolders;
    procedure PopulateSettings;
    procedure PopulateDefaultMediaFormats;
    procedure PopulateMediaFormats(Formats: TStrings; View: TListView);
    procedure AddNewFormat(View: TListView);
    procedure AddLocation(const FolderName, FolderPath: String);
    procedure DeleteLocation(Folder: TAHMMediaFolder);
    procedure SaveSettings;
    procedure SaveMediaFormats(Formats: TStrings; View: TListView);
    procedure InitialiseControls;
    procedure UpdateKeybindingSelection;
    procedure UpdateFilterSelection;
    procedure UpdateGUIState;
    procedure DroppedFiles(var Message: TWMDROPFILES); message WM_DROPFILES;
  public
    { Public declarations }
    function ConfigureSettings: Boolean;
  end;


implementation

{$R *.dfm}

uses ShellAPI, AHMTypes, AHMConsts, ApplicationManager, ActionManager,
     KeyBindingForm, ScreenSaver, PluginPropsForm, FolderForm, InternetHelper,
     Colour, ColourRenderer, Logging, Utilities;


//-----------------------------------------------------------------------------
//------------------------------ Form Events ----------------------------------
//-----------------------------------------------------------------------------

procedure TfrmSettings.FormCreate(Sender: TObject);
begin
  // Create DirectShow enumerator instance
  FEnumerator := TAHMDirectShowEnumerator.Create;

  // Create long term manager object for media folders
  FManager := TAHMMediaFolderManager.Create;

  // Create buffer bitmap to render header gradient
  FHeader := TBitmap.Create;

  // Populate default media formats & description
  PopulateDefaultMediaFormats;
end;

procedure TfrmSettings.FormDestroy(Sender: TObject);
begin
  // Release DirectShow enumerator instance
  FEnumerator.Free;

  // Free long term media folder manager object
  FManager.Free;

  // Free header gradient buffer
  FHeader.Free;
end;


//-----------------------------------------------------------------------------
//---------------------------- Button Events ----------------------------------
//-----------------------------------------------------------------------------

procedure TfrmSettings.btViewProfileClick(Sender: TObject);
begin
  // Display keybindings for current selection
  with TfrmKeyBindings.Create(Self) do
  try
    PopupParent := Application.MainForm;
    BindingName := ecKeyBinding.Text;
    ShowModal;
  finally
    Free;
  end;
end;

procedure TfrmSettings.btBrowseClick(Sender: TObject);
var
  i: Integer;
  FileName, Extensions: String;
begin
  // Build list of supported image types
  Extensions := 'Image files|*' + EXT_IMAGES[0];
  for i  := 1 to High(EXT_IMAGES) do
    Extensions := Extensions + ';*' + EXT_IMAGES[i];

  // Browse for screensaver image file
  FileName := edScreenSaver.Text;
  if ChooseFile('Select an image for screen saver', Extensions,
                '', ExtractFilePath(FileName), FileName, False) then
    edScreenSaver.Text := FileName;
end;

procedure TfrmSettings.btDVDVideoPropsClick(Sender: TObject);
begin
  // Show properties dialog for DVD video codec
  FEnumerator.ShowDVDVideoProperties(Self.Handle, ecDVDVideo.Text);
end;

procedure TfrmSettings.btDVDAudioPropsClick(Sender: TObject);
begin
  // Show properties dialog for DVD audio codec
  FEnumerator.ShowDVDAudioProperties(Self.Handle, ecDVDAudio.Text);
end;

procedure TfrmSettings.btAddVideoClick(Sender: TObject);
begin
  // Prompt for and add new video format
  AddNewFormat(lvVideo);
end;

procedure TfrmSettings.btAddAudioClick(Sender: TObject);
begin
  // Prompt for and add new audio format
  AddNewFormat(lvAudio);
end;

procedure TfrmSettings.btDeleteVideoClick(Sender: TObject);
begin
  // Delete current selection from video format list
  with lvVideo do
    if Assigned(Selected) then Items.Delete(Selected.Index);
  UpdateGUIState;
end;

procedure TfrmSettings.btDeleteAudioClick(Sender: TObject);
begin
  // Delete current selection from audio format list
  with lvAudio do
    if Assigned(Selected) then Items.Delete(Selected.Index);
  UpdateGUIState;
end;

procedure TfrmSettings.btPluginAddClick(Sender: TObject);
begin
//
end;

procedure TfrmSettings.btPluginDeleteClick(Sender: TObject);
begin
//
end;

procedure TfrmSettings.btPluginPropertiesClick(Sender: TObject);
begin
  // Show properties for selected plugin
  with lvPlugins do
    if Assigned(Selected) then TfrmPluginProperties.ShowProperties(Selected.SubItems[1]);
end;

procedure TfrmSettings.btConfigurePluginClick(Sender: TObject);
begin
  // Show configuration for selected plugin
  with lvPlugins do
    if Assigned(Selected) then TfrmPluginProperties.ShowConfiguration(Selected.SubItems[1]);
end;

procedure TfrmSettings.btAddFolderClick(Sender: TObject);
var
  NewFolder: TAHMMediaFolder;
begin
  // Configure folder form for adding new folder
  if TfrmFolderDetails.ShowFolder(fmAdd, NewFolder, CurrentType) then
  begin
    FManager.Locations.Folders[CurrentType].AddFolder(NewFolder);
    FManager.Modified := True;

    // Ensure GUI is in a consistent state
    PopulateFolders;
    UpdateGUIState;
  end;
end;

procedure TfrmSettings.btEditFolderClick(Sender: TObject);
var
  Folder: TAHMMediaFolder;
begin
  // Validate that we have a selected list item
  if not Assigned(lvFolders.Selected) then Exit;

  Folder := TAHMMediaFolder(lvFolders.Selected.Data);
  if TfrmFolderDetails.ShowFolder(fmEdit, Folder, CurrentType) then
  begin
    FManager.Modified := True;
    PopulateFolders;
  end;
end;

procedure TfrmSettings.btDeleteFolderClick(Sender: TObject);
var
  Folder: TAHMMediaFolder;
begin
  // Confirm delete then reload list view
  if MessageDlg(MSG_CONFIRM_DEL, mtConfirmation, [mbYes, mbNo], 0) = mrYes then
  begin
    // Delete folder item for selected group
    Folder := TAHMMediaFolder(lvFolders.Selected.Data);
    DeleteLocation(Folder);

    // Ensure GUI is in a consistent state
    PopulateFolders;
    UpdateGUIState;
  end;
end;

procedure TfrmSettings.btSaveClick(Sender: TObject);
begin
  // Store modified settings in preferences manager
  SaveSettings;
end;

//-----------------------------------------------------------------------------
//------------------------- Miscellaneous Events ------------------------------
//-----------------------------------------------------------------------------

procedure TfrmSettings.TreeViewChange(Sender: TObject; Node: TTreeNode);
begin
  // Show relevant notebook page for selected tree node
  if Assigned(TreeView.Selected) and (TreeView.Selected.SelectedIndex >= 0) then
    Notebook.PageIndex := TreeView.Selected.SelectedIndex;

  // Rebuild media folder list if required
  if CurrentType <> ftUnknown then PopulateFolders;

  // Update header panel
  UpdateGUIState;
end;

procedure TfrmSettings.lvAudioClick(Sender: TObject);
begin
  // Keep gui consistent when we select/unselect an extension
  if not FUpdatingList then UpdateGUIState;
end;

procedure TfrmSettings.lvVideoClick(Sender: TObject);
begin
  // Keep gui consistent when we select/unselect an extension
  if not FUpdatingList then UpdateGUIState;
end;

procedure TfrmSettings.lvPluginsClick(Sender: TObject);
begin
  // Keep gui consistent when we select/unselect a plugin
  if not FUpdatingList then UpdateGUIState;
end;

procedure TfrmSettings.lvFoldersClick(Sender: TObject);
begin
  // Keep gui consistent when we select/unselect a folder
  if not FUpdatingList then UpdateGUIState;
end;

procedure TfrmSettings.lbAudioSupportClick(Sender: TObject);
begin
  DisplayBrowserCodecs;
end;

procedure TfrmSettings.lbSupportClick(Sender: TObject);
begin
  DisplayBrowserSupport;
end;

procedure TfrmSettings.lbPluginsLinkClick(Sender: TObject);
begin
  DisplayBrowserPlugins;
end;

procedure TfrmSettings.lvAudioColumnClick(Sender: TObject;
  Column: TListColumn);
begin
  // Sort list contents by clicked column. Toggle between descending
  // and ascending sort if same column is clicked again
  if FSortAudioColumn = Column.Index then
    FSortAudioDescending := not FSortAudioDescending
  else
  begin
    FSortAudioColumn := Column.Index;
    FSortAudioDescending := False;
  end;
  lvAudio.AlphaSort;
end;

procedure TfrmSettings.lvAudioCompare(Sender: TObject; Item1,
  Item2: TListItem; Data: Integer; var Compare: Integer);
var
  i: Integer;
begin
  // Compare appropriate column
  if FSortAudioColumn = 0 then
    Compare := CompareText(Item1.Caption, Item2.Caption)
  else
  begin
   i := Pred(FSortAudioColumn);
   Compare := CompareText(Item1.SubItems[i], Item2.SubItems[i]);
  end;

  // Invert comparison for descending search
  if FSortAudioDescending then Compare := -Compare;
end;

procedure TfrmSettings.lvVideoColumnClick(Sender: TObject;
  Column: TListColumn);
begin
  // Sort list contents by clicked column. Toggle between descending
  // and ascending sort if same column is clicked again
  if FSortVideoColumn = Column.Index then
    FSortVideoDescending := not FSortVideoDescending
  else
  begin
    FSortVideoColumn := Column.Index;
    FSortVideoDescending := False;
  end;
  lvVideo.AlphaSort;
end;

procedure TfrmSettings.lvVideoCompare(Sender: TObject; Item1,
  Item2: TListItem; Data: Integer; var Compare: Integer);
var
  i: Integer;
begin
  // Compare appropriate column
  if FSortVideoColumn = 0 then
    Compare := CompareText(Item1.Caption, Item2.Caption)
  else
  begin
   i := Pred(FSortVideoColumn);
   Compare := CompareText(Item1.SubItems[i], Item2.SubItems[i]);
  end;

  // Invert comparison for descending search
  if FSortVideoDescending then Compare := -Compare;
end;

procedure TfrmSettings.pbHeaderPaint(Sender: TObject);
begin
  with Sender as TPaintBox do
  begin
    // Rebuild gradient if dimensions change, or on first render
    if (FHeader.Width <> Width) or (FHeader.Height <> Height) then
    begin
      FHeader.Width := Width;
      FHeader.Height := Height;
      with ColourRendererInstance do
        BlendBitmap(TAHMCanvasColour.GradientLo, TAHMCanvasColour.GradientHi,
                    TAHMCanvasColour.GradientLo, TAHMCanvasColour.GradientHi,
                    FHeader);
    end;

    // Render gradient
    Canvas.Draw(0, 0, FHeader);

    // Render caption
    Canvas.Brush.Style := bsClear;
    Canvas.TextOut(12, 9, Notebook.ActivePage);
  end;
end;

procedure TfrmSettings.lvPluginsColumnClick(Sender: TObject;
  Column: TListColumn);
begin
  // Sort list contents by clicked column. Toggle between descending
  // and ascending sort if same column is clicked again
  if FSortPluginColumn = Column.Index then
    FSortPluginDescending := not FSortPluginDescending
  else
  begin
    FSortPluginColumn := Column.Index;
    FSortPluginDescending := False;
  end;
  lvPlugins.AlphaSort;
end;

procedure TfrmSettings.lvFoldersColumnClick(Sender: TObject;
  Column: TListColumn);
begin
  // Sort list contents by clicked column. Toggle between descending
  // and ascending sort if same column is clicked again
  if FSortFolderColumn = Column.Index then
    FSortFolderDescending := not FSortFolderDescending
  else
  begin
    FSortFolderColumn := Column.Index;
    FSortFolderDescending := False;
  end;
  lvFolders.AlphaSort;
end;

procedure TfrmSettings.lvPluginsCompare(Sender: TObject; Item1,
  Item2: TListItem; Data: Integer; var Compare: Integer);
var
  i: Integer;
begin
  // Compare appropriate column
  if FSortPluginColumn = 0 then
    Compare := CompareText(Item1.Caption, Item2.Caption)
  else
  begin
   i := Pred(FSortPluginColumn);
   Compare := CompareText(Item1.SubItems[i], Item2.SubItems[i]);
  end;

  // Invert comparison for descending search
  if FSortPluginDescending then Compare := -Compare;
end;

procedure TfrmSettings.lvFoldersCompare(Sender: TObject; Item1,
  Item2: TListItem; Data: Integer; var Compare: Integer);
var
  i: Integer;
begin
  // Compare appropriate column
  if FSortFolderColumn = 0 then
    Compare := CompareText(Item1.Caption, Item2.Caption)
  else
  begin
   i := Pred(FSortFolderColumn);
   Compare := CompareText(Item1.SubItems[i], Item2.SubItems[i]);
  end;

  // Invert comparison for descending search
  if FSortFolderDescending then Compare := -Compare;
end;

procedure TfrmSettings.ecDVDDecoderChange(Sender: TObject);
begin
  // Update state of filter properties button
  UpdateFilterSelection;
end;

procedure TfrmSettings.ecKeybindingChange(Sender: TObject);
begin
  // Update description for selected keybinding
  UpdateKeybindingSelection;
end;

procedure TfrmSettings.cbMaximumFPSClick(Sender: TObject);
begin
  // Update maximum fps control
  UpdateGUIState;
end;

procedure TfrmSettings.cbUseProxyClick(Sender: TObject);
begin
  // Update proxy controls
  UpdateGUIState;
end;

procedure TfrmSettings.ecScreenSaverChange(Sender: TObject);
begin
  // Update screensaver custom edit
  UpdateGUIState;
end;

//-----------------------------------------------------------------------------
//--------------------- User Interface Configuration --------------------------
//-----------------------------------------------------------------------------

function TfrmSettings.CurrentType: TAHMMediaFolderType;
begin
  // Set default result - unknown type
  Result := ftUnknown;

  // Use tree selection to determine current folder type
  if Assigned(TreeView.Selected) then
    case TreeView.Selected.StateIndex of
      0: Result := ftMusic;
      1: Result := ftVideo;
      2: Result := ftImage;
    end;
end;

function TfrmSettings.ConfigureSettings: Boolean;
begin
  // Populate controls from current application settings
  PopulateSettings;

  // Initialise display of form controls
  InitialiseControls;

  // Now show form modally
  ShowModal;
  Result := FUpdated;
end;

procedure TfrmSettings.PopulateSettings;
var
  sKeyProfile: String;
  List: TStringList;
begin
  // Populate preferred media formats
  List := TStringList.Create;
  FUpdatingList := True;
  try
    List.CommaText := ApplicationManagerInstance.PreferencesManager.AudioFormats;
    PopulateMediaFormats(List, lvAudio);
    List.CommaText := ApplicationManagerInstance.PreferencesManager.VideoFormats;
    PopulateMediaFormats(List, lvVideo);
  finally
    List.Free;
    FUpdatingList := False;
  end;

  // Populate plugins
  PopulatePlugins;

  // Load media locations when we start
  FManager.LoadLocations;

  // Populate settings from preferences manager
  with ApplicationManagerInstance.PreferencesManager do
  begin
    // Populate screen saver configuration
    if ScreenSaverFile = SAVER_IMAGE_EMPTY then ecScreenSaver.ItemIndex := 2
    else if ScreenSaverFile = '' then ecScreenSaver.ItemIndex := 0
    else
    begin
      ecScreenSaver.ItemIndex := 1;
      edScreenSaver.Text := ScreenSaverFile;
    end;

    // Populate the easy ones
    ecMouse.ItemIndex := Ord(MouseCursor);
    ecRButton.ItemIndex := Ord(MouseRightAction);
    ecTextureFormat.ItemIndex := Ord(TextureFormat);
    ecFontQuality.ItemIndex := Ord(FontQuality);
    ecAudioMixing.ItemIndex := Ord(AudioMixing);
    cbExclusive.Checked := Fullscreen;
    ecVideoRenderer.ItemIndex := Ord(VideoRenderer);
    ecVideoDevice.ItemIndex := DisplayAdapter;
    cbHideBorder.Checked := HideWindowBorder;
    cbMaximize.Checked := MaximizeWindow;
    cbGroupDirs.Checked := GroupDirectories;
    cbDebug.Checked := ShowDiagnostics;
    cbSaveConsole.Checked := SaveConsoleHistory;
    cbSaveGraph.Checked := SaveGraphToFile;
    cbCheckVersion.Checked := CheckUpdates;
    cbBalloonTips.Checked := HideBalloonTips;
    sKeyProfile := KeyProfileName;

    // Populate maximum fps settings
    cbMaximumFPS.Checked := (ScreenRefresh <> 0);
    if ScreenRefresh = 0 then udMaximumFPS.Position := 30
                         else udMaximumFPS.Position := ScreenRefresh;

    // Populate preferred codecs using directshow enumerator
    FEnumerator.EnumerateAudioRenderers(ecAudioRenderer.Items);
    ecAudioRenderer.ItemIndex := ecAudioRenderer.Items.IndexOf(AudioDevice);
    if ecAudioRenderer.ItemIndex < 0 then ecAudioRenderer.ItemIndex := 0;
    FEnumerator.EnumerateAudioCaptureSources(ecAudioCapture.Items);
    ecAudioCapture.ItemIndex := ecAudioCapture.Items.IndexOf(AudioCapture);
    if ecAudioCapture.ItemIndex < 0 then ecAudioCapture.ItemIndex := 0;
    FEnumerator.EnumerateDVDAudioFilters(ecDVDAudio.Items);
    ecDVDAudio.ItemIndex := ecDVDAudio.Items.IndexOf(DVDAudioFilter);
    if ecDVDAudio.ItemIndex < 0 then ecDVDAudio.ItemIndex := 0;
    FEnumerator.EnumerateDVDVideoFilters(ecDVDVideo.Items);
    ecDVDVideo.ItemIndex := ecDVDVideo.Items.IndexOf(DVDVideoFilter);
    if ecDVDVideo.ItemIndex < 0 then ecDVDVideo.ItemIndex := 0;
    UpdateFilterSelection;

    // Populate theme settings from theme manager
    ecCurrentTheme.Items.Assign(ApplicationManagerInstance.ThemeManager.InstalledThemes);
    ThemeName := ApplicationManagerInstance.ThemeManager.CheckThemeExists(ThemeName);
    ecCurrentTheme.ItemIndex := ecCurrentTheme.Items.IndexOf(ThemeName);
  end;

  with ApplicationManagerInstance.ActionManager do
  begin
    // Populate keybinding settings from action manager
    ecKeyBinding.Items.Assign(KeyProfiles);
    ecKeyBinding.ItemIndex := KeyProfiles.IndexOf(sKeyProfile);
    UpdateKeybindingSelection;
  end;

  // Populate proxy settings
  with InternetHelperInstance do
  begin
    cbUseProxy.Checked := ProxyServer <> '';
    edProxyServer.Text := ProxyServer;
    udProxyPort.Position := ProxyPort;
    edProxyUser.Text := ProxyUserName;
    edProxyPass.Text := ProxyPassword;
  end;

  // Populate logging preferences
  ecLogLevel.ItemIndex := Ord(LoggingInstance.LoggingLevel);
  cbCache.Checked := LoggingInstance.LogFileCacheing;
end;

procedure TfrmSettings.PopulatePlugins;
var
  i: Integer;
begin
  lvPlugins.Items.BeginUpdate;
  try
    with ApplicationManagerInstance.PluginManager do
      for i := 0 to Pred(Plugins.Count) do
      begin
        if Plugins.Plugin[i].ParseDescriptor then
          with lvPlugins.Items.Add do
          begin
            Caption := Plugins.Plugin[i].Name;
            SubItems.Add(Plugins.Plugin[i].Description);
            SubItems.Add(Plugins.Plugin[i].InternalName);
            Checked := Plugins.Plugin[i].Enabled;
            Data := Plugins.Plugin[i];
          end;
      end;
  finally
    lvPlugins.Items.EndUpdate;
  end;
end;

procedure TfrmSettings.PopulateFolders;
var
  List: TAHMMediaFolderList;
  i: Integer;
begin
  with lvFolders do
  begin
    FUpdatingList := True;
    Items.BeginUpdate;
    try
      Items.Clear;

      // Determine appropriate folder list to populate
      List := FManager.Locations.Folders[CurrentType];

      for i := 0 to Pred(List.Count) do
        with Items.Add do
        begin
          Data := List.Folders[i];
          Caption := List.Folders[i].FolderName;
          SubItems.Add(List.Folders[i].FolderPath);
        end;

      lvFolders.AlphaSort;
    finally
      Items.EndUpdate;
      FUpdatingList := False;
    end;
  end;
end;

procedure TfrmSettings.PopulateDefaultMediaFormats;
type
  TMediaFormat = array[1..2] of String;
const
  AUDIO_FMTS: array[1..10] of TMediaFormat =
    (('CDA',  'CD Audio (Audio CD Tracks)'),
     ('FLAC', 'Free Lossless Audio Codec *'),
     ('M4A',  'iTunes DRM free (Apple Audio Codec) *'),
     ('MID',  'MIDI Audio'),
     ('MIDI', 'MIDI Audio'),
     ('MP3',  'MP3 Audio'),
     ('OGG',  'Ogg Vorbis Audio *'),
     ('RA',   'Real Audio *'),
     ('WAV',  'WAV Audio'),
     ('WMA',  'Windows Media Audio'));
  VIDEO_FMTS: array[1..12] of TMediaFormat =
    (('AVI',  'AVI Video Container'),
     ('DIVX', 'DivX Video *'),
     ('FLV',  'Flash Video *'),
     ('MKV',  'Matroska Video Container *'),
     ('MOV',  'Apple Quicktime Video *'),
     ('MPEG', 'MPEG-2 Video *'),
     ('MPG',  'MPEG-2 Video *'),
     ('MP4',  'MPEG-4 Video Container *'),
     ('OGM',  'Ogg Theora Video *'),
     ('RM',   'Real Media *'),
     ('VOB',  'DVD Video Object (Unencrypted only) *'),
     ('WMV',  'Windows Media Video'));

  procedure PopulateFormats(Formats: array of TMediaFormat; View: TListView);
  var
    i: Integer;
  begin
    View.Items.BeginUpdate;
    try
    for i := Low(Formats) to High(Formats) do
      with View.Items.Add do
      begin
        Caption := Formats[i, 1];
        SubItems.Add(Formats[i, 2]);
      end;
    finally
      View.Items.EndUpdate;
    end;
  end;
begin
  PopulateFormats(AUDIO_FMTS, lvAudio);
  PopulateFormats(VIDEO_FMTS, lvVideo);
end;

procedure TfrmSettings.PopulateMediaFormats(Formats: TStrings; View: TListView);
var
  i: Integer;
  Item: TListItem;
  Extension: String;
begin
  View.Items.BeginUpdate;
  try
    for i := 0 to Pred(Formats.Count) do
    begin
      // Remove preceeding . character from extension
      Extension := Copy(Formats[i], 2, Pred(Length(Formats[i])));

      // Match media extension with listview caption
      Item := View.FindCaption(0, Extension, False, True, False);

      // If found then check item, otherwise add it
      if Assigned(Item) then
        Item.Checked := True
      else
        with View.Items.Add do
        begin
          Caption := Extension;
          SubItems.Add(Extension + ' File');
          Checked := True;
        end;
    end;
  finally
    View.Items.EndUpdate;
  end;
end;

procedure TfrmSettings.AddNewFormat(View: TListView);
var
  Extension: String;
  Item: TListItem;
begin
  // Prompt for new extension and remove '.' chars
  Extension := UpperCase(InputBox('Add New Format', 'Enter new extension to show within mediate:', ''));
  while Pos('.', Extension) > 0 do Delete(Extension, Pos('.', Extension), 1);
  if Extension = '' then Exit;

  // If item already in list just check it otherwise add new item
  Item := View.FindCaption(0, Extension, False, True, False);
  if not Assigned(Item) then
  begin
    Item := View.Items.Add;
    Item.Caption := Extension;
    Item.SubItems.Add(Extension + ' File');
  end;
  Item.Checked := True;
end;

procedure TfrmSettings.AddLocation(const FolderName, FolderPath: String);
var
  i: Integer;
  Folder: TAHMMediaFolder;
begin
  // First check that we don't already have passed Folder listed
  with FManager.Locations.Folders[CurrentType] do
  begin
    for i := 0 to Pred(Count) do
    begin
      Folder := Folders[i];
      if (Folder.FolderName = FolderName) and (Folder.FolderPath = FolderPath) then Exit;
    end;

    // Okay to add passed folder - it's unique
    AddFolder(FolderName, FolderPath, False);
  end;

  FManager.Modified := True;
end;

procedure TfrmSettings.DeleteLocation(Folder: TAHMMediaFolder);
var
  List: TAHMMediaFolderList;
begin
  // Determine appropriate folder list to perform deletion
  List := FManager.Locations.Folders[CurrentType];
  List.DeleteFolder(Folder);
  FManager.Modified := True;
end;

procedure TfrmSettings.SaveSettings;
var
  List: TStringList;
begin
  FUpdated := True;

  // Save preferred media formats
  List := TStringList.Create;
  try
    SaveMediaFormats(List, lvAudio);
    ApplicationManagerInstance.PreferencesManager.AudioFormats := List.CommaText;
    SaveMediaFormats(List, lvVideo);
    ApplicationManagerInstance.PreferencesManager.VideoFormats := List.CommaText;
  finally
    List.Free;
  end;

  // Save updated media locations
  FManager.SaveLocations;

  // Update settings in preferences manager
  with ApplicationManagerInstance.PreferencesManager do
  begin
    // Update screen saver configuration
    if ecScreenSaver.ItemIndex = 0 then ScreenSaverFile := ''
    else if ecScreenSaver.ItemIndex = 2 then ScreenSaverFile := SAVER_IMAGE_EMPTY
    else ScreenSaverFile := edScreenSaver.Text;

    // Update maximum fps settings
    if cbMaximumFPS.Checked then ScreenRefresh := udMaximumFPS.Position
                            else ScreenRefresh := 0;

    // Update easy settings
    with ecAudioRenderer do if ItemIndex > 0 then AudioDevice := Text else AudioDevice := '';
    with ecAudioCapture do if ItemIndex > 0 then AudioCapture := Text else AudioCapture := '';
    with ecDVDAudio do if ItemIndex > 0 then DVDAudioFilter := Text else DVDAudioFilter := '';
    with ecDVDVideo do if ItemIndex > 0 then DVDVideoFilter := Text else DVDVideoFilter := '';
    MouseCursor := TAHMMouseCursor(ecMouse.ItemIndex);
    MouseRightAction := TAHMMouseRightAction(ecRButton.ItemIndex);
    TextureFormat := TAHMTextureFormat(ecTextureFormat.ItemIndex);
    FontQuality := TAHMFontQuality(ecFontQuality.ItemIndex);
    AudioMixing := TAHMAudioMixing(ecAudioMixing.ItemIndex);
    Fullscreen := cbExclusive.Checked;
    VideoRenderer := TAHMVideoRenderer(ecVideoRenderer.ItemIndex);
    DisplayAdapter := ecVideoDevice.ItemIndex;
    HideWindowBorder := cbHideBorder.Checked;
    MaximizeWindow := cbMaximize.Checked;
    GroupDirectories := cbGroupDirs.Checked;
    ShowDiagnostics := cbDebug.Checked;
    SaveConsoleHistory := cbSaveConsole.Checked;
    SaveGraphToFile := cbSaveGraph.Checked;
    KeyProfileName := ecKeyBinding.Text;
    ThemeName := ecCurrentTheme.Text;
    CheckUpdates := cbCheckVersion.Checked;
    HideBalloonTips := cbBalloonTips.Checked;
  end;

  // Update proxy settings
  with InternetHelperInstance do
    if cbUseProxy.Checked then
    begin
      ProxyServer := edProxyServer.Text;
      ProxyPort := udProxyPort.Position;
      ProxyUserName := edProxyUser.Text;
      ProxyPassword := edProxyPass.Text;
    end
    else ProxyServer := '';

  // Update logging preferences
  LoggingInstance.LogFileCacheing := cbCache.Checked;
  LoggingInstance.LoggingLevel := TLogLevel(ecLogLevel.ItemIndex);
end;

procedure TfrmSettings.SaveMediaFormats(Formats: TStrings; View: TListView);
var
  i: Integer;
begin
  // Clear format list
  Formats.Clear;

  // Add each checked format extension with prefix '.'
  for i := 0 to Pred(View.Items.Count) do
    if View.Items[i].Checked then
      Formats.Add('.' + View.Items[i].Caption);
end;

procedure TfrmSettings.InitialiseControls;
begin
  // Ensure first page of notebook is selected
  Notebook.PageIndex := 0;

  // Expand treeview
  TreeView.FullExpand;

  // Start GUI in a consistent state
  UpdateGUIState;
end;

procedure TfrmSettings.UpdateKeybindingSelection;
var
  Profile: TAHMKeyProfile;
begin
  with ApplicationManagerInstance.ActionManager do
  begin
    // Populate keybinding settings from action manager
    Profile := KeyProfiles.GetKeyProfile(ecKeyBinding.Text);
    if Assigned(Profile) then lbInputDescription.Caption := Profile.DisplayText
                         else lbInputDescription.Caption := '';
  end;
end;

procedure TfrmSettings.UpdateFilterSelection;
begin
  // Determine whether our selected DVD video filter has configurable properties
  with ecDVDVideo do
    btDVDVideoProps.Enabled := (ItemIndex > 0) and FEnumerator.HasDVDVideoProperties(Text);

  // Determine whether our selected DVD audio filter has configurable properties
  with ecDVDAudio do
    btDVDAudioProps.Enabled := (ItemIndex > 0) and FEnumerator.HasDVDAudioProperties(Text);
end;

procedure TfrmSettings.UpdateGUIState;
begin
  // Configure custom screensaver edit
  edScreenSaver.Enabled := (ecScreenSaver.ItemIndex = 1);
  btBrowse.Enabled := edScreenSaver.Enabled;

  // Configure directx fps edit
  edMaximumFPS.Enabled := cbMaximumFPS.Checked;
  udMaximumFPS.Enabled := cbMaximumFPS.Checked;

  // Enable/Disable actions depending on current selection
  btDeleteAudio.Enabled := Assigned(lvAudio.Selected);
  btDeleteVideo.Enabled := Assigned(lvVideo.Selected);
  btDeleteFolder.Enabled := Assigned(lvFolders.Selected);
  btEditFolder.Enabled := Assigned(lvFolders.Selected);

  // Configure plugin actions depending on current plugin selection
  btPluginAdd.Enabled := False;
//  btPluginAdd.Enabled := Assigned(lvPlugins.Selected);
  btPluginDelete.Enabled := False;
//  btPluginDelete.Enabled := Assigned(lvPlugins.Selected);
  btPluginProperties.Enabled := Assigned(lvPlugins.Selected);
  btConfigurePlugin.Enabled := Assigned(lvPlugins.Selected);

  // Configure panel/groupbox caption
  pbHeader.Invalidate;
  case CurrentType of
    ftMusic: gbFolders.Caption := 'Music Folders';
    ftVideo: gbFolders.Caption := 'Video Folders';
    ftImage: gbFolders.Caption := 'Picture Folders';
  end;

  // Update drag & dropped folder acceptance status (only on media folders)
  DragAcceptFiles(Handle, CurrentType <> ftUnknown);

  // Configure proxy control state
  edProxyServer.Enabled := cbUseProxy.Checked;
  edProxyUser.Enabled := cbUseProxy.Checked;
  edProxyPort.Enabled := cbUseProxy.Checked;
  edProxyPass.Enabled := cbUseProxy.Checked;
  udProxyPort.Enabled := cbUseProxy.Checked;
end;

procedure TfrmSettings.DroppedFiles(var Message: TWMDROPFILES);
var
  i, Count: LongInt;
  Buffy: array[0..MAX_PATH] of char;
  Name: String;
begin
  // Pass $ffffffff to retrieve file count
  Count := DragQueryFile(Message.Drop, $ffffffff, nil, 0);

  // Iterate through each dropped file looking for directories
  for i := 0 to Pred(Count) do
    if (DragQueryFile(Message.Drop, i, @Buffy, SizeOf(Buffy)) > 0) then
    begin
      if ExtractFileExt(Buffy) <> '' then Continue;
      Name := ExtractFileName(Buffy);
      AddLocation(Name, Buffy);
    end;

  // Refresh to show new locations
  PopulateFolders;
  UpdateGUIState;
end;


end.
